package org.yeasy.redundance.util;


import org.yeasy.common.util.CommonBeanUtil;
import org.yeasy.redundance.bean.MyEntry;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * 单体冗余更新代理.
 * The type Redundance proxy.
 *
 * @param <T> the type parameter
 * @author yangyishe
 * @date 2023年04月11日 16:26
 */
public class RedundanceProxy<T> implements FillRedundanceInterface<T>{
    private T target;

    /**
     * Instantiates a new Redundance proxy.
     *
     * @param target the target
     * @author yangyishe
     * @date 2023年04月11日 16:26
     */
    public RedundanceProxy(T target) {
        this.target = target;
    }

    @Override
    public FillRedundanceInterface<T> fillRedundanceFromValue(Object value, List<String> redundanceKeyList) {
        for (String redundanceKey : redundanceKeyList) {
            CommonBeanUtil.invokeSetter(this.target,redundanceKey,value);
        }
        return this;
    }

    @Override
    public FillRedundanceInterface<T> fillRedundanceFromSingle(Object info, List<String> redundanceKeyList) {
        Map<String, String> mapTargetKey2InfoKey=new HashMap<>();
        for (String redundanceKey : redundanceKeyList) {
            String[] rddKeys = redundanceKey.split(" ");
            if (rddKeys.length==2) {
                mapTargetKey2InfoKey.put(rddKeys[1],rddKeys[0]);
            }else{
                mapTargetKey2InfoKey.put(redundanceKey,redundanceKey);
            }
        }
        return fillRedundanceFromSingle(info,mapTargetKey2InfoKey);
    }

    @Override
    public FillRedundanceInterface<T> fillRedundanceFromSingle(Object info, Map<String, String> targetKey2InfoKeyMap) {
        if(info!=null){
            for (Map.Entry<String, String> infoKey2TargetKey : targetKey2InfoKeyMap.entrySet()) {
                Object infoVal = CommonBeanUtil.invokeGetter(info, infoKey2TargetKey.getValue());
                CommonBeanUtil.invokeSetter(this.target,infoKey2TargetKey.getKey(),infoVal);
            }
        }
        return this;
    }

    /**
     * Fill redundant.
     *
     * @param infoList          the info list
     * @param key               the key
     * @param redundanceKeyList the redundance key list
     */
    @Override
    public FillRedundanceInterface<T> fillRedundanceFromList(List<?> infoList, String key, List<String> redundanceKeyList){
        Map.Entry<String, String> keySourceEntry = new MyEntry<>(key, key);


        return fillRedundanceFromList(infoList,keySourceEntry,redundanceKeyList);
    }

    @Override
    public FillRedundanceInterface<T> fillRedundanceFromList(List<?> infoList, Map.Entry<String, String> keySource, List<String> redundanceKeyList) {
        Map<String, String> mapTargetKey2InfoKey=new HashMap<>();
        for (String redundanceKey : redundanceKeyList) {
            String[] rddKeys = redundanceKey.split(" ");
            if (rddKeys.length==2) {
                mapTargetKey2InfoKey.put(rddKeys[1],rddKeys[0]);
            }else{
                mapTargetKey2InfoKey.put(redundanceKey,redundanceKey);
            }
        }
        return fillRedundanceFromList(infoList,keySource,mapTargetKey2InfoKey);
    }

    /**
     * Fill redundant.
     * @param infoList             the info list
     * @param keySource            the key source
     * @param targetKey2InfoKeyMap the info key 2 target key map
     */
    @Override
    public FillRedundanceInterface<T> fillRedundanceFromList(List<?> infoList, Map.Entry<String,String> keySource, Map<String,String> targetKey2InfoKeyMap){
        String[] sourceKeys = keySource.getKey().split(" ");
        String[] sourceValues=keySource.getValue().split(" ");
        BiFunction<Object,String[], String> calcInfo2Key = (u, ks) -> Arrays.stream(ks)
                .map(k-> {
                    Object v=CommonBeanUtil.invokeGetter(u,k);
                    return v!=null?v.toString():null;
                })
                .reduce("", (s, s2) -> {
                    if(s==null||s2==null){
                        return null;
                    }
                    return (s+" "+s2).trim();
                });
        Map<String, ?> mapInfoId2Entity = infoList.stream().collect(Collectors.toMap(item->calcInfo2Key.apply(item,sourceKeys), item -> item));
        Object info=mapInfoId2Entity.get(calcInfo2Key.apply(this.target,sourceValues));
        if(info!=null){
            for (Map.Entry<String, String> infoKey2TargetKey : targetKey2InfoKeyMap.entrySet()) {
                Object infoVal = CommonBeanUtil.invokeGetter(info, infoKey2TargetKey.getValue());
                CommonBeanUtil.invokeSetter(this.target,infoKey2TargetKey.getKey(),infoVal);
            }
        }
        return this;
    }
}
